/*
* Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
* Description: mbuf_yuv_sender.h head file
*/
#ifndef MODULES_CAMERA_SAMPLE_IMAGE_SENDER_MBUF_YUV_SENDER_H_
#define MODULES_CAMERA_SAMPLE_IMAGE_SENDER_MBUF_YUV_SENDER_H_

#include "image_sender_base.h"
#include "mdc/cam/camera/cameradecodedmbufserviceinterface_skeleton.h"
#include "util.h"
#include "core/types.h"
#include "driver/ascend_hal.h"
#include "securec.h"

using namespace std;
using namespace cv;

using MbufSkeleton = mdc::cam::camera::skeleton::CameraDecodedMbufServiceInterfaceSkeleton;
class MbufYuvSender : public ImageSenderBase<MbufSkeleton>
{
private:
    poolHandle handle;
    float lastTime = 0.0F;
    chrono::high_resolution_clock::time_point sec;//-t used
    std::string sourceYuvPath;
    FILE *fp_yuv;

public:
    MbufYuvSender() = default;
    MbufYuvSender(uint32_t id, cv::Size s, std::string dir) : ImageSenderBase<MbufSkeleton>(id, s, dir)
    {
        sourceYuvPath = dir;
        if (dir.back() != '/') {
            sourceYuvPath += string("/") + "yuvSource.yuv";
        } else {
            sourceYuvPath += "yuvSource.yuv";
        }
#ifdef SAVE_SENDER_FILE
        fp_yuv = fopen(sourceYuvPath.c_str(), "wb+");
#endif
        sec = chrono::high_resolution_clock::now();
    }
    ~MbufYuvSender()
    {
        if (dataSkeleton != nullptr) {
            dataSkeleton->StopOfferService();
            dataSkeleton = nullptr;
        }
        if (handle != nullptr) {
            halBuffDeletePool(handle);
        }
    };

    bool RegisterSender()
    {
        std::cout << "Begin register MBUF image sender." << endl;
        auto result = ImageSenderBase<MbufSkeleton>::RegisterSender();
        std::cout << "Finished to register MBUF image sender." << endl;
        return result;
    }

    int32_t BufCreatePool(const uint32_t blkSize, const uint32_t blkNum)
    {
        constexpr uint32_t alignSize = 32U;
        mpAttr attr;
        attr.devid = 0;
        attr.mGroupId = 0;
        attr.blkSize = ((blkSize + alignSize) - 1U) & (~(alignSize - 1));
        attr.blkNum = blkNum;
        attr.align = alignSize;
        attr.hugePageFlag = 1U;
        const int32_t ret = halBuffCreatePool(&attr, &handle);
        if (ret != DRV_ERROR_NONE) {
            std::cout << "halBuffCreatePool error: " << ret << std::endl;
            return -1;
        }
        return 0;
    }

    int32_t CopyToMbuf(uintptr_t &mbuf, const uint8_t *src, const uint32_t len) const
    {
        Mbuf *pMbuf = nullptr;
        int32_t ret = halMbufAllocByPool(handle, &pMbuf);
        if (ret != DRV_ERROR_NONE) {
            std::cout << "halMbufAllocByPool error: " << ret << std::endl;
            return -1;
        }
        if (pMbuf == nullptr) {
            std::cout << "halMbufAllocByPool mbuf null" << std::endl;
            return -1;
        }
        void *dst = nullptr;
        uint64_t bufSize = 0U;
        ret = halMbufGetDataPtr(pMbuf, &dst, &bufSize);
        if (ret != 0 || dst == nullptr) {
            std::cout << "MbufGetDataPtr error: " << ret << std::endl;
            halBuffFree(pMbuf);
            return -1;
        }
        if (len > bufSize) {
            std::cout << "ERROR: Src data length[" << len << "] > mbuf size[" << bufSize << "]" << std::endl;
            halBuffFree(pMbuf);
            return -1;
        }
        if (memcpy_s(
                    reinterpret_cast<uint8_t *>(dst), static_cast<uint64_t>(bufSize), src, static_cast<uint64_t>(len)) !=
                0) {
            std::cout << "ERROR: Copy src date to mbuf error!" << std::endl;
            halBuffFree(pMbuf);
            return -1;
        }
        ret = halMbufSetDataLen(pMbuf, len);
        if (ret != DRV_ERROR_NONE) {
            std::cout << "halMbufSetDataLen error: " << ret << std::endl;
            halBuffFree(pMbuf);
            return -1;
        }
        mbuf = reinterpret_cast<uintptr_t>(pMbuf);
        return 0;
    }

    void FillImageAndSend(std::string yuvPath, uint32_t seq, void *g_stopFlag) override
    {
        (void)g_stopFlag;
        std::string path = dir;
        std::string postfix = comPara.fileType;
        bool isLoop = comPara.loop;
        cout << "dir: " << dir << "\t" << "postfix :" << postfix << endl;
        std::vector<std::string> files;
        int32_t fileCnt = getFilesBySort(path, files, comPara.fileType);
        if (fileCnt == 0) {
            cout << "PATH:" << path << " has no files!" << endl;
            return;
        }
        do {
            for (auto &file : files) {
                int32_t w = imgSize.width;
                int32_t h = imgSize.height;
                FILE *yuv_file;
                yuv_file = fopen(file.c_str(), "rb");
                if (yuv_file == nullptr) {
                    cout << "Open yuv file failed!" << endl;
                    return;
                }
                int32_t frameSize = w * h * 3 / 2;
                fseek(yuv_file, 0, SEEK_END);
                int32_t fileSize = ftell(yuv_file);
                rewind(yuv_file);
                int32_t frameCount = fileSize / frameSize;
                char *yuvBuff = (char *)malloc(frameSize * sizeof(char));
                for (int32_t i = 0; i < frameCount; i++) {
                    Mat YUV420NV12;
                    YUV420NV12.create(h * 1.5, w, CV_8UC1);
                    memset(yuvBuff, 0, frameSize);
                    fread(yuvBuff, sizeof(char), frameSize, yuv_file);
                    memcpy(YUV420NV12.data, yuvBuff, frameSize);
                    auto imagePub = dataSkeleton->cameraDecodedMbufEvent.Allocate();
                    imagePub->CameraHeader.Seq = seq;
                    imagePub->FrameType = static_cast < decltype(imagePub->FrameType) > (Adsfi::HAF_IMAGE_YUV420SP_NV12_UINT8);
                    imagePub->Width = w;
                    imagePub->Height = h;
                    imagePub->DataSize = frameSize;
                    imagePub->CameraHeader.FrameId = file;
                    uintptr_t imgdata{};
                    (void)CopyToMbuf(imgdata, YUV420NV12.data, frameSize);
                    if (imgdata == reinterpret_cast<uintptr_t>(nullptr)) {
                        std::cout << "ERROR: CopyToMbuf failed!" << std::endl;
                        return;
                    }
                    imagePub->RawData = reinterpret_cast<rawBuffer *>(imgdata);
#ifdef SAVE_SENDER_FILE
                    fwrite(YUV420NV12.data, 1, frameSize, fp_yuv);
#endif
                    if (comPara.time.empty()) {
                        timeval now;
                        gettimeofday(&now, NULL);
                        imagePub->CameraHeader.Stamp.Sec = now.tv_sec;
                        imagePub->CameraHeader.Stamp.Nsec = now.tv_usec * 1000U;
                    } else {
                        time_t timeStamp = convertTimeStr2TimeStamp(comPara.time);
                        auto t1 = chrono::high_resolution_clock::now();
                        int64_t duration = (t1 - sec).count() / 1000000000.0;
                        imagePub->CameraHeader.Stamp.Sec = timeStamp + duration;
                        printf("timeStamp=%ld\n", timeStamp + duration);
                    }
                    dataSkeleton->cameraDecodedMbufEvent.Send(move(imagePub));
                    cout << "Begin send image. seq: " << seq << ", frameID: " << frameID << " path: " << file << endl;
                    seq++;
                    if (comPara.frequency != "") {
                        int32_t freq = atoi(comPara.frequency.c_str());
                        int32_t sleepTime = 1000000 / freq;
                        usleep(sleepTime);
                    } else {
                        usleep(1000000 / 30);//30hz
                    }
                }
                free(yuvBuff);
            }
        } while (isLoop);
#ifdef SAVE_SENDER_FILE
        fclose(fp_yuv);
#endif
    }
};
#endif
